﻿using Avalonia.Controls;
using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Runtime;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LightCAD.MathLib;

namespace LightCAD.Drawing.Actions
{
    public class RayAction : ElementAction
    {
        public static LcCreateMethod[] CreateMethods;

        static RayAction()
        {
            CreateMethods = new LcCreateMethod[1];
            CreateMethods[0] = new LcCreateMethod()
            {
                Name = "2P",
                Description = "两点创建射线",
                Steps = new LcCreateStep[]
                {
                    new LcCreateStep { Name=  "Step0", Options= "指定起点：" },
                    new LcCreateStep { Name=  "Step1", Options= "指定通过点：" },
                }
            };
        }

        internal static void Initilize()
        {
            ElementActions.Ray = new RayAction();
            LcDocument.ElementActions.Add(BuiltinElementType.Ray, ElementActions.Ray);
        }

        private Vector2 originPoint { get; set; }
        private Vector2 thruPoint { get; set; }
        private PointInputer inputer { get; set; }

        private RayAction() { }
        public RayAction(IDocumentEditor docEditor) : base(docEditor)
        {
            docEditor.CommandCenter.WriteInfo("命令：Ray");
        }

        public async void ExecCreate(string[] args = null)
        {
            this.StartCreating();
            this.inputer = new PointInputer(this.docEditor);
            var curMethod = CreateMethods[0];
        Step0:
            var step0 = curMethod.Steps[0];
            var result0 = await inputer.Execute(step0.Options);

            if (inputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }

            if (result0.ValueX == null)
            {
                if (result0.Option != null)
                {

                }
                else
                {
                    goto Step1;
                }
            }
            this.originPoint = (Vector2)result0.ValueX;
        Step1:
            var step1 = curMethod.Steps[1];
            var result1 = await inputer.Execute(step1.Options);
            if (inputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }
            if (result1.ValueX == null)
            {
                if (result1.Option == " ")
                {
                    goto End;
                }
                else if (result1.Option != null)
                {
                    docEditor.CommandCenter.WriteInfo(SR.PointError);
                    goto Step1;
                }
                else
                {
                    goto Step1;
                }
            }

            this.thruPoint = (Vector2)result1.ValueX;
            this.CreateRay();
            goto Step1;
        End:
            this.inputer = null;
            this.EndCreating();
        }

        public override void Cancel()
        {
            base.Cancel();
            this.vportRt.SetCreateDrawer(null);
            this.originPoint = null;
            this.thruPoint = null;
        }

        public void CreateRay()
        {
            if (this.originPoint != null)
            {
                this.docRt.Document.ModelSpace.AddRay(this.originPoint, this.thruPoint);
            }
        }

        public override void DrawAuxLines(SKCanvas canvas)
        {
            if (originPoint != null)
            {
                var mp = this.vportRt.PointerMovedPosition.ToVector2d();
                var wcs_mp = this.vportRt.ConvertScrToWcs(mp);
                DrawRay(canvas, originPoint, wcs_mp);
            }
        }

        public override void DrawTemp(SKCanvas canvas)
        {
            //if (this.originPoint != null)
            //{
            //    for (var i = 0; i < this.points.Count; i++)
            //    {
            //        var p = this.points[i];
            //        DrawRay(canvas, this.originPoint, p);
            //    }
            //}
        }
        private void DrawRay(SKCanvas canvas, Vector2 p0, Vector2 p1)
        {
            if (p0.Equals(p1))
                return;

            var box = this.vportRt.Renderer.GetWcsClipBox();

            var rayDir = (p1 - p0).Normalize();
            var ray = new LcRay(p0, rayDir);
            var interPs = LcGeoUtils.IntersectRayBox(ray, box);

            var interP = interPs.OrderByDescending(P => Vector2.Distance(p0, P)).FirstOrDefault();

            var sk_pre = this.vportRt.ConvertWcsToScr(p0).ToSKPoint();
            var sk_p = this.vportRt.ConvertWcsToScr(interP).ToSKPoint();
            //get Layer color 
            using (var paint = new SKPaint { Color = this.vportRt.GetAuxColorValue(), IsStroke = true })
            {
                canvas.DrawLine(sk_pre, sk_p, paint);
            }
        }

        public override void Draw(SKCanvas canvas, LcElement element, Matrix3 matrix)
        {
            var ray = element as LcRay;
            var box = this.vportRt.Renderer.GetWcsClipBox();
            var interPs =  LcGeoUtils.IntersectRayBox(ray, box);
            var interP = interPs.OrderByDescending(P => Vector2.Distance(ray.StartPoint, P)).FirstOrDefault();

            var mstart = matrix.MultiplyPoint(ray.StartPoint);
            var mend = matrix.MultiplyPoint(interP);

            var start = this.vportRt.ConvertWcsToScr(mstart).ToSKPoint();
            var end = this.vportRt.ConvertWcsToScr(mend).ToSKPoint();

            bool isDragCopy = (matrix != Matrix3.Zero);
            var pen = this.GetDrawPen(ray, isDragCopy);
            if (pen == Constants.defaultPen)
            {
                //TODO:这里可以考虑将实线用颜色做KEY，对SKPaint进行缓存
                pen.Color = new SKColor(element.GetColorValue());
                pen.IsStroke = true;
            }

            canvas.DrawLine(start, end, pen);
        }
        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var ray = element as LcRay;
            var grips = new List<ControlGrip>();
            var gripStart = new ControlGrip
            {
                Element = ray,
                Name = "Start",
                Position = ray.StartPoint
            };
            grips.Add(gripStart);
            var gripEnd = new ControlGrip
            {
                Element = ray,
                Name = "End",
                Position = ray.StartPoint + (ray.Direction * 100)
            };
            grips.Add(gripEnd);
            return grips.ToArray();
        }

        private string _gripName;
        private Vector2 _position;
        private LcRay _ray;
        public override void SetDragGrip(LcElement element, string gripName, Vector2 position, bool isEnd)
        {
            var ray = element as LcRay;
            _ray = ray;
            if (!isEnd)
            {
                _gripName = gripName;
                _position = position;
            }
            else
            {
                if (gripName == "Start")
                    ray.Set(startPoint: position);
                else if (gripName == "End")
                {
                    _ray.Set(dir: (position - ray.StartPoint).Normalize());
                }
            }
        }

        public override void DrawDragGrip(SKCanvas canvas)
        {
            if (_ray == null) return;

            var start = this.vportRt.ConvertWcsToScr(_ray.StartPoint);
            if (_gripName == "Start")
                start = this.vportRt.ConvertWcsToScr(_position);

            var end = this.vportRt.ConvertWcsToScr(_ray.StartPoint + (_ray.Direction * 100));
            if (_gripName == "End")
                end = this.vportRt.ConvertWcsToScr(_position);

            canvas.DrawLine(start.ToSKPoint(), end.ToSKPoint(), Constants.draggingPen);
        }

        public override List<PropertyObserver> GetPropertyObservers()
        {
            return new List<PropertyObserver>()
            {
                 new PropertyObserver()
                 {
                      Name = "PointX",
                      DisplayName = "基点 X 坐标",
                      CategoryName = "Geometry",
                      CategoryDisplayName = "几何图形",
                      Getter = (ele) => (ele as LcRay).StartPoint.X,
                       Setter = (ele, value) =>
                       {
                        var ray = (ele as LcRay);
                        var x = Convert.ToDouble(value);
                        var p = new Vector2(x, ray.StartPoint.Y);
                        ray.Set(startPoint: p);
                        }
                 },
                   new PropertyObserver()
                 {
                      Name = "PointY",
                      DisplayName = "基点 Y 坐标",
                      CategoryName = "Geometry",
                      CategoryDisplayName = "几何图形",
                      Getter = (ele) => (ele as LcRay).StartPoint.Y,
                       Setter = (ele, value) =>
                       {
                        var ray = (ele as LcRay);
                        var y = Convert.ToDouble(value);
                        var p = new Vector2( ray.StartPoint.X,y);
                        ray.Set(startPoint: p);
                        }
                 },
                    new PropertyObserver()
                {
                    Name = "PointZ",
                    DisplayName = "基点 Z 坐标",
                    CategoryName = "Geometry",
                    CategoryDisplayName = "几何图形",
                    Getter = (ele) => 0
                },

                    new PropertyObserver()
                 {
                      Name = "SecondPointX",
                      DisplayName = "第二点 X 坐标",
                      CategoryName = "Geometry",
                      CategoryDisplayName = "几何图形",
                      Getter = (ele) => (ele as LcRay).Direction.X+(ele as LcRay).StartPoint.X,
                       Setter = (ele, value) =>
                       {
                        var ray = (ele as LcRay);
                        var x = Convert.ToDouble(value);
                        var sp = new Vector2(x, ray.Direction.Y);
                        ray.Set(dir: sp);
                        }
                 },
                       new PropertyObserver()
                 {
                      Name = "SecondPointY",
                      DisplayName = "第二点 Y 坐标",
                      CategoryName = "Geometry",
                      CategoryDisplayName = "几何图形",
                      Getter = (ele) => (ele as LcRay).Direction.Y+(ele as LcRay).StartPoint.Y,
                       Setter = (ele, value) =>
                       {
                        var ray = (ele as LcRay);
                        var y = Convert.ToDouble(value);
                        var sp = new Vector2( ray.Direction.X,y);
                        ray.Set(dir: sp);
                        }
                 },

                new PropertyObserver()
                {
                    Name = "SecondPointZ",
                    DisplayName = "第二点 Z 坐标",
                    CategoryName = "Geometry",
                    CategoryDisplayName = "几何图形",
                    Getter = (ele) => 0
                },

                 new PropertyObserver()
                {
                    Name = "NormalX",
                    DisplayName = "法向 X 坐标",
                    CategoryName = "Geometry",
                    CategoryDisplayName = "几何图形",
                    Getter = (ele) => (ele as LcRay).Direction.X,

                },
                new PropertyObserver()
                {
                    Name = "NormalY",
                    DisplayName = "法向 Y 坐标",
                    CategoryName = "Geometry",
                    CategoryDisplayName = "几何图形",
                    Getter = (ele) => (ele as LcRay).Direction.Y,

                },
                new PropertyObserver()
                {
                    Name = "NormalZ",
                    DisplayName = "法向 Z 坐标",
                    CategoryName = "Geometry",
                    CategoryDisplayName = "几何图形",
                    Getter = (ele) => 0
                },
            };
        }
    }
}
